from absl import app, flags
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from pathlib import Path
import pandas as pd
import datetime
import logging
import tempfile
import io

from coin.strategy.dmm_strat.analysis.exchange.binance.binance_dmm_analysis import BinanceDMMAnalysis

FLAGS = flags.FLAGS


class BinanceDMMExporter(object):
  def __init__(self, dmm_type, score_dir, req_path, output_dir):
    self._dmm_type = dmm_type
    self._market_type = self._dmm_type if self._dmm_type == 'spot' else 'futures'
    self._analysis = BinanceDMMAnalysis(self._dmm_type, score_dir, req_path)
    self._output_dir = output_dir

  def _percentage_format(self, df):
    df_out = df.copy()
    for col in df_out:
      df_out.loc[:, col] = df_out.loc[:, col].apply(lambda x: str(
          round(100 * x, 2)) + '%' if pd.notnull(x) else str(0) + '%')
    return df_out

  def export_report_xls(self, start_time, end_time):
    result_data = self._analysis.get_results(start_time, end_time)
    df_scores = pd.concat([result_data['symbol'],
                           result_data['turnover_adjusted_weight'],
                           result_data['maker_market_share'],
                           result_data['qualified_ratio'],
                           result_data['score_weight']],
                           axis=1)
    df_scores = df_scores.set_index('symbol')
    df_scores.index.name = None
    df_scores.loc["Total"] = [result_data['total']['turnover_adjusted_weight'],
                              result_data['total']['maker_market_share'],
                              result_data['total']['qualified_ratio'],
                              result_data['total']['score_weight']]
    qualif_coverage = str(round(100 * result_data['total']['quali_coverage'], 2)) + '%'
    df_potential_scores = result_data['potential_scores']
    df_potential_scores = df_potential_scores.set_index('symbol')
    df_potential_scores.index.name = None
    df_potential_scores = df_potential_scores.drop(['score_weight'], axis=1)
    df_turnover = result_data['trader_turnover']
    df_turnover_contrib = df_turnover.copy()
    for col in df_turnover_contrib:
      if df_turnover_contrib.loc['Total', col] != 0:
        df_turnover_contrib.loc[:, col] = df_turnover_contrib.loc[:, col].apply(
            lambda x: x / df_turnover_contrib.loc['Total', col])
    df_scores = self._percentage_format(df_scores)
    df_potential_scores = self._percentage_format(df_potential_scores)
    df_turnover_contrib = self._percentage_format(df_turnover_contrib)

    writer = pd.ExcelWriter(
        f'{self._output_dir}/binance_{self._dmm_type}_{start_time.strftime("%Y%m%d")}_{end_time.strftime("%Y%m%d")}_summary.xlsx', engine='xlsxwriter')
    txt = writer.book.add_format({"font_size": 9, 'align': 'right'})
    hdr = writer.book.add_format({'bold': True, "font_size": 14})
    col_hdr = writer.book.add_format(
        {'bold': True, "font_size": 11, 'align': 'center', 'border': 1})
    df_scores.to_excel(writer, sheet_name='Score_weight_summary', startrow=1)
    df_potential_scores.to_excel(
        writer, sheet_name='Score_weight_summary', startrow=len(df_scores) + 5)
    df_turnover.to_excel(writer, sheet_name='Trader_marketshare_contribution', startrow=1)
    df_turnover_contrib.to_excel(
        writer, sheet_name='Trader_marketshare_contribution', startrow=len(df_turnover) + 5)
    sheet1 = writer.sheets['Score_weight_summary']
    sheet2 = writer.sheets['Trader_marketshare_contribution']
    sheet4 = writer.book.add_worksheet("Summary figure")

    sheet1.set_column('A:Z', 25, txt)
    sheet1.write('A1', "Score weight:", hdr)
    sheet1.write(f'F{len(df_scores)+1}', "Qualification coverage", col_hdr)
    sheet1.write(f'F{len(df_scores)+2}', qualif_coverage)
    sheet1.write(f'A{len(df_scores)+5}',
                 f'Potential score weight with market share threshold = 1% and qualified ratio threshold = 75%:', hdr)
    sheet2 = writer.sheets['Trader_marketshare_contribution']
    sheet2.set_column('A:BA', 20, txt)
    sheet2.write('A1', "Adjusted turnover:", hdr)
    sheet2.write(f'A{len(df_turnover)+5}', "Percentage:", hdr)

    x_prolong = 1 if self._market_type == 'spot' else 2
    fig, ax = plt.subplots(figsize=(25 * x_prolong, 15))
    fig.suptitle("Symbols score ranking", fontsize=14)
    labels_sorted = result_data['symbol']
    scores_sorted = 100 * result_data['turnover_adjusted_weight']
    market_share_sorted = 100 * result_data['maker_market_share']
    ax.bar(labels_sorted, scores_sorted)
    ax.set_xlabel('Symbols')
    ax.set_ylabel('Percentage')
    ax.invert_xaxis()
    scores_sorted.plot(color='k', xlim=ax.get_xlim())
    market_share_sorted.plot(color='darkorange', xlim=ax.get_xlim())
    for x, y in zip(labels_sorted, market_share_sorted):
      label = "{:.2f}%".format(y)
      plt.annotate(label,
                   (x, y),
                   textcoords="offset points",
                   xytext=(0, 5),
                   ha='center',
                   color='darkorange')
    rects = ax.patches
    labels = ["%.2f" % s + "%" for s in scores_sorted]
    for rect, label in zip(rects, labels):
      height = rect.get_height()
      ax.text(rect.get_x() + rect.get_width() / 2, height, label, ha='center', va='bottom')
    plt.legend(['Turnover adjusted weight', 'Market share'])
    image_data = io.BytesIO()
    plt.savefig(image_data, format='png', dpi=300)
    sheet4.insert_image('A1', '', {'image_data': image_data})
    writer.save()
    logging.info(f'summary file generated/saved successfully in directory {self._output_dir}')

  def export_daily_plot(self, start_time, end_time):
    result_data = self._analysis.get_results(start_time, end_time)
    daily_summary = result_data['daily_summary']
    daily_volume_pct = result_data['daily_volume_pct']
    weekly_volume_pct = result_data['weekly_volume_pct']
    daily_ranks = daily_summary['daily_ranking']
    weekly_ranks = daily_summary['weekly_ranking']
    adj_turnovers = daily_summary['daily_trader_adj_turnover']
    qualified_ratios = daily_summary['daily_daily_adj_qualified_ratio']
    xlabels = []
    cur_date = start_time
    while cur_date <= end_time:
      xlabels.append(cur_date)
      cur_date += datetime.timedelta(days=1)
    f = plt.figure(figsize=(10, 15))
    plt.subplot(4, 1, 1)
    plt.plot(xlabels, daily_ranks, color='k')
    plt.plot(xlabels, weekly_ranks, color='b')
    plt.gca().invert_yaxis()
    plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=1))
    map_tier = {0: '0~25', 1: '25~50', 2: '50~75', 3: '75~100', 4: 'N/A'}
    for x, y in zip(xlabels, daily_ranks):
      plt.annotate(map_tier[y] + '%' if self._dmm_type == 'coin_margined' else str(y) + '%',
                   (x, y),
                   textcoords="offset points",
                   xytext=(0, 5),
                   ha='center',
                   color='k')
    for x, y in zip(xlabels, weekly_ranks):
      plt.annotate(map_tier[y] + '%' if self._dmm_type == 'coin_margined' else str(y) + '%',
                   (x, y),
                   textcoords="offset points",
                   xytext=(0, 5),
                   ha='center',
                   color='b')
    plt.legend(['Daily ranking', 'Cumulative weekly ranking'])
    plt.subplot(4, 1, 2)
    turnover = pd.concat(adj_turnovers, axis=1, sort=False)
    width = 0.8
    prev_sum = 0
    for i in range(len(turnover)):
      plt.bar(xlabels, turnover.iloc[i], width, bottom=prev_sum)
      prev_sum += turnover.iloc[i]
    plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=1))
    plt.legend(turnover.index)
    plt.subplot(4, 1, 3)
    plt.plot(xlabels, qualified_ratios, color='b')
    for x, y in zip(xlabels, qualified_ratios):
      plt.annotate(str(round(100 * y, 2)) + '%',
                   (x, y),
                   textcoords="offset points",
                   xytext=(0, 5),
                   ha='center',
                   color='b')
    plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=1))
    plt.legend(['Daily adjusted qualified ratio'])
    if self._market_type == 'futures':
      plt.subplot(4, 1, 4)
      plt.plot(xlabels, daily_volume_pct, color='k')
      plt.plot(xlabels, weekly_volume_pct, color='b')
      for x, y in zip(xlabels, daily_volume_pct):
        plt.annotate(str(y) + '%',
                     (x, y),
                     textcoords="offset points",
                     xytext=(0, 5),
                     ha='center',
                     color='k')
      for x, y in zip(xlabels, weekly_volume_pct):
        plt.annotate(str(y) + '%',
                     (x, y),
                     textcoords="offset points",
                     xytext=(0, 5),
                     ha='center',
                     color='b')
      plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=1))
      plt.legend(['Daily maker volume percentage', 'Cumulative weekly maker volume percentage'])
    f.savefig(f'{self._output_dir}/binance_{start_time.strftime("%Y%m%d")}_{end_time.strftime("%Y%m%d")}_{self._dmm_type}_daily_plot.pdf')
    logging.info(
        f'daily summary figure generated/saved successfully in directory {self._output_dir}')


def main(_):
  pd.options.mode.chained_assignment = None
  if FLAGS.start_date is None and FLAGS.end_date is None:
    end_date = datetime.date.today() - datetime.timedelta(days=1)
    start_date = end_date - datetime.timedelta(days=6)
  else:
    start_date = datetime.datetime.strptime(FLAGS.start_date, '%Y%m%d').date()
    end_date = datetime.datetime.strptime(FLAGS.end_date, '%Y%m%d').date()
  if FLAGS.dmm_type is None:
    raise ValueError('no flag dmm_type provided')
  if FLAGS.score_dir is None:
    raise ValueError('no flag score_dir provided')
  if FLAGS.req_filepath is None:
    raise ValueError('no flag req_filepath provided')
  if FLAGS.output_dir is None:
    FLAGS.output_dir = tempfile.mkdtemp()
  else:
    Path(FLAGS.output_dir).mkdir(parents=True, exist_ok=True)
  exporter = BinanceDMMExporter(FLAGS.dmm_type, FLAGS.score_dir,
                                FLAGS.req_filepath, FLAGS.output_dir)
  exporter.export_report_xls(start_date, end_date)
  exporter.export_daily_plot(start_date, end_date)


if __name__ == '__main__':
  flags.DEFINE_string('start_date', None, '%Y%m%d')
  flags.DEFINE_string('end_date', None, '%Y%m%d')
  flags.DEFINE_string('dmm_type', None, '')
  flags.DEFINE_string('score_dir', None, '')
  flags.DEFINE_string('req_filepath', None, '')
  flags.DEFINE_string('output_dir', None, '')
  app.run(main)
